/*
 * @Author: wuqingchun
 * @Date: 2024-04-15 09:12:08
 * @Description: C++ 引用
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-04-28 17:21:35
 */
#include <iostream>

void test_reference()
{
    // error: ‘refValue’ declared as reference but not initialized
    // int& refValue;

    // 引用在定义的时候就必须进行初始化
    int iValue;
    int& refValue = iValue;

    // 虽然引用在最终实现上也是一个实际的内存变量，也有自己的内存地址，保存的是原始变量（引用绑定的变量）地址
    // 但是由于编译器对引用做了特殊处理
    // 1）定义时必须初始化，不然会编译报错
    // 2）对引用取地址，获取的是原始变量的地址（普通变量取地址操作转换后的汇编指令为lea，引用变量取地址操作转换后的汇编指令为 mov)
    // 3) 引用一旦绑定了一个变量，不能再绑定其他变量

    // 引用在概念上可以理解为原始变量的一个别名，
    // 一旦定义了原始变量的一个引用，后续针对该引用的任何操作，都等价于操作原始变量本身
    // 1) 赋值操作，修改的是原始变量的值
    refValue = 100;
    std::cout << "   iValue: " << iValue << std::endl;

    // 2) 取值操作，获取的是原始变量的值
    std::cout << " refValue: " << refValue << std::endl;
    
    // 3) 取地址操作，获取的是原始变量的值
    std::cout << "   iValue Address: " << &iValue << std::endl;
    std::cout << " refValue Address: " << &refValue << std::endl;

    // 引用一旦绑定了一个变量，不能再被修改
    int iValue2 = 200;
    refValue = iValue2; // 将 iValue 赋值成 iValue2


    // 只读引用，其值不能被修改
    const int& refConst = 100;         // 使用右值初始化只读引用
    const int& refConst2 = iValue2;    // 使用可修改左值初始化 只读引用

    // 定义只读变量
    const int iValue3 = 300;
    // 给一个只读引用赋值，编译错误
    //iValue3 = 301;
    const int& refConst3 = iValue3;  // 使用只读左值初始化 只读引用

    // error: assignment of read-only reference ‘refConst2’
    // 给一个只读引用赋值，编译错误
    // refConst2 = 200;
}

template<typename T>
void ShowType(const char* pcType)
{
    if (std::is_lvalue_reference<T>::value)
    {
        std::cout << pcType << ": lvalue_reference" << std::endl;
    }
    else if (std::is_integral<T>::value)
    {
        std::cout << pcType << ": integral" << std::endl;
    }
    else if (std::is_rvalue_reference<T>::value)
    {
        std::cout << pcType << ": rvalue_reference" << std::endl;
    }
    else 
    {
        std::cout << pcType << ": unknown type" << std::endl;
    }

    if (std::is_lvalue_reference<T&>::value)
    {
        std::cout << pcType << " &: lvalue_reference" << std::endl;
    }
    else if (std::is_integral<T&>::value)
    {
        std::cout << pcType << " &: integral" << std::endl;
    }
    else if (std::is_rvalue_reference<T&>::value)
    {
        std::cout << pcType << " &: rvalue_reference" << std::endl;
    }
    else 
    {
        std::cout << pcType << " &: unknown type" << std::endl;
    }

    if (std::is_lvalue_reference<T&&>::value)
    {
        std::cout << pcType << " &&: lvalue_reference" << std::endl;
    }
    else if (std::is_integral<T&&>::value)
    {
        std::cout << pcType << " &&: integral" << std::endl;
    }
    else if (std::is_rvalue_reference<T&&>::value)
    {
        std::cout << pcType << " &&: rvalue_reference" << std::endl;
    }
    else 
    {
        std::cout << pcType << " &&: unknown type" << std::endl;
    }
}

void test_reference_collapsing()
{
    int iValue;
    int& refValue = iValue;

    ShowType<int>("int");
    ShowType<int&>("int&");
    ShowType<int&&>("int&&");
}

// 和int &m类型参数无法重载
// void RunCode(int m) 
// { 
//     std::cout << "lvalue" << std::endl; 
// }

void RunCode(int &&m) 
{
    std::cout << "rvalue ref" << std::endl; 
}

void RunCode(int &m) 
{ 
    std::cout << "lvalue ref" << std::endl; 
}

void RunCode(const int &&m) 
{ 
    std::cout << "const rvalue ref" << std::endl; 
}

void RunCode(const int &m) 
{ 
    std::cout << "const lvalue ref" << std::endl; 
}

// 转发模板函数
template <typename T>
void ForwardFunction(T&& arg)
{
    //RunCode(static_cast<T&&>(arg));
    RunCode(std::forward<T>(arg));
}

void test_perfact_forward()
{
    int iValue = 10;
    const int iValue2 = 20;
    const int& refValue2 = iValue2;
    int&& refValue3 = 100;
    
    ForwardFunction(10);
    ForwardFunction(iValue);
    ForwardFunction(iValue2);
    ForwardFunction(refValue2);
    ForwardFunction(refValue3);
    ForwardFunction(std::move(200));
}


template <typename T>
void func(T&& arg)
{
    if (std::is_lvalue_reference<decltype(arg)>::value)
    {
        std::cout << "lvalue_reference" << std::endl;
    }
    else if (std::is_rvalue_reference<decltype(arg)>::value)
    {
        std::cout << "rvalue_reference" << std::endl;
    }
}

int main(int argc, char** argv)
{
    
    test_reference();
    std::cout << std::endl;

    test_reference_collapsing();
    std::cout << std::endl;

    test_perfact_forward();

    int iValue = 0;
    int& refLref = iValue;
    int&& refRRef = 0;

    func<int>(0);
    func<int&>(refLref);
    func<int&>(refRRef);
    func<int&&>(std::move(0));

    return 0;
}